﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Xml.Serialization;

namespace SwimmingCompetitions
{
    /// <summary>
    /// Keeps all data for a competition, including all races, participants, etc.
    /// </summary>
    [Serializable]
    public class CompetitionModel
    {
        [Serializable]
        public class RaceData
        {
            public string Title;

            public int Distance;
            public SwimmingGlobals.Mode Mode;
            public SwimmingGlobals.Participants Participants;

            public DataTable Table;

            public RaceData(int distance, SwimmingGlobals.Mode mode, SwimmingGlobals.Participants participants)
            {
                Distance = distance;
                Mode = mode;
                Participants = participants;
                Title = string.Format("{0} - {1} ({2}м)",
                                      participants.GetDescription<SwimmingGlobals.Participants>(),
                                      mode.GetDescription<SwimmingGlobals.Mode>(), 
                                      distance);
                Table = new DataTable(Title);
            }

            /// <summary>
            /// Constructor only for serialization.
            /// </summary>
            public RaceData()
            {
            }
        }

        public List<RaceData> Races = new List<RaceData>();

        public List<string> Participants;
        public List<string> Teams;

        #region Columns

        public const int ColumnsCount = 8;

        public const int ColumnSwim = 0;
        public const int ColumnTrack = 1;
        public const int ColumnParticipant = 2;
        public const int ColumnTeam = 3;
        public const int ColumnDefaultTime = 4;
        public const int ColumnRank = 5;
        public const int ColumnTime = 6;
        public const int ColumnScore = 7;

        private static DataColumn[] getColumns()
        {
            return new[]
                       {
                           new DataColumn("Заплыв", typeof (int)),
                           new DataColumn("Дорожка", typeof (int)),
                           new DataColumn("Участник"),
                           new DataColumn("Команда"),
                           new DataColumn("Заявленное время", typeof(TimeSpan)),
                           new DataColumn("Разряд", typeof(Rank)),
                           new DataColumn("Время", typeof(TimeSpan)),
                           new DataColumn("Очки", typeof (int))
                       };
        }

        #endregion

        /// <summary>
        /// Track indices for results. First is the index for best result.
        /// </summary>
        private static readonly int[] Tracks = new[] { 2, 3, 1, 4, 0, 5 };
        
        public RaceData AddRace(int distance, SwimmingGlobals.Mode mode, SwimmingGlobals.Participants participants)
        {
            var data = new RaceData(distance, mode, participants);
            Logger.Instance.Log("Adding race \'" + data.Title + "\'");
            //duplicate columns for each table
            data.Table.Columns.AddRange(getColumns());
            Races.Add(data);
            return data;
        }
        
        public void Save(string fileName)
        {
            Logger.Instance.Log("Saving competition to: " + fileName);
            var serializer = new XmlSerializer(typeof(CompetitionModel));
            using (var stream = new FileStream(fileName, FileMode.Create))
            {
                serializer.Serialize(stream, this);
            }
            Logger.Instance.Log("Competition saved");
        }

        public void Load(string fileName)
        {
            Participants.Clear();
            Teams.Clear();
            Logger.Instance.Log("Loading competition from: " + fileName);
            CompetitionModel data;
            var serializer = new XmlSerializer(typeof(CompetitionModel));
            using (var stream = new FileStream(fileName, FileMode.Open))
            {
                data = (CompetitionModel)serializer.Deserialize(stream);
            }
            Races.Clear();

            Participants = data.Participants.Distinct().ToList();
            Teams = data.Teams.Distinct().ToList();
            Races.AddRange(data.Races);
            Logger.Instance.Log("Competition loaded");
        }

        public CompetitionModel()
        {
            Participants = new List<string>();
            Teams = new List<string>();
            
#if DEBUG
            Participants.AddRange(new[] {"Иванов","Петров","Сидоров","Николаева","Ленин","Киров","Кротова"});
            Teams.AddRange(new[]
                               {
                                   "СГАУ Факультет 1", "СГАУ Факультет 2", "СГАУ Факультет 3", "СГАУ Факультет 4",
                                   "СГАУ Факультет 5", "СГАУ Факультет 6", "СГАУ Факультет 7"
                               });
#endif
        }

        public void GenerateAllRaces()
        {
            foreach (var race in Races)
            {
                generateRaces(race);
            }
        }

        private static void generateRaces(RaceData model)
        {
            sort(model);
            var count = model.Table.Rows.Count;
            var newModel = model.Table.Copy();
            model.Table.Clear();
            
            for (var i = 0; i < count; i++)
            {
                model.Table.Rows.Add(newModel.Rows[i].ItemArray);
                model.Table.Rows[i][ColumnSwim] = i / 6 + 1;
                model.Table.Rows[i][ColumnTrack] = Tracks[5-i%6] + 1;
            }

            //if table has unfilled race
            var lastTrack = model.Table.Rows[count-1][ColumnSwim];
            var cnt = 0;
            for (var i = count - 1; i >= 0 && model.Table.Rows[i][ColumnSwim].Equals(lastTrack); --i, ++cnt)
            {
                model.Table.Rows[i][ColumnTrack] = Tracks[cnt] + 1;
            }
        }

        private static void sort(RaceData model)
        {
            var count = model.Table.Rows.Count;    //оптимизируем
            for (var i = 0; i < count; i++)
            {
                for (var j = 0; j < count - 1; j++)
                {
                    if (model.Table.Rows[j][ColumnDefaultTime] is DBNull || model.Table.Rows[j + 1][ColumnDefaultTime] is DBNull ||
                        (TimeSpan)model.Table.Rows[j][ColumnDefaultTime] < (TimeSpan)model.Table.Rows[j + 1][ColumnDefaultTime])
                    {
                        exchangeValueRows(model.Table.Rows[j], model.Table.Rows[j+1]);
                    }
                }       
            }
        }

        private static void exchangeValueRows(DataRow rows1, DataRow rows2)
        {
            for (var i = 0; i < rows1.ItemArray.Length; i++)
            {
                var buf = rows2[i];
                rows2[i] = rows1[i];
                rows1[i] = buf;
            }
        }
    }
}
